package com.ibeetl.admin.aod.util;

import org.apache.commons.compress.archivers.ArchiveOutputStream;
import org.apache.commons.compress.archivers.ArchiveStreamFactory;
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
import org.apache.commons.compress.archivers.zip.ZipArchiveInputStream;
import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * ZIP文件解压缩工具
 */
public class ZipFileUtil {

    private static final Logger logger = LoggerFactory.getLogger(ZipFileUtil.class);

    public static int BUFFER_SIZE = 1024 * 10;

    /**
     * 对文件进行压缩
     *
     * @param zipFileName       压缩文件名
     * @param srcDir            要压缩的目录
     * @param encoding          采用的编码
     * @param deleteSrcFileFlag 压缩完是否删除源目录
     */
    public static void zip(String zipFileName, String srcDir, String encoding, boolean deleteSrcFileFlag) {
        File srcPath = new File(srcDir);
        if (srcPath.exists() && srcPath.isDirectory()) {
            List<File> files = Arrays.asList(srcPath.listFiles());
            zip(zipFileName, files, encoding, deleteSrcFileFlag);
        }
    }

    /**
     * 对文件进行压缩
     *
     * @param zipFileName       压缩文件名
     * @param files             要压缩的文件
     * @param encoding          采用的编码
     * @param deleteSrcFileFlag 压缩完是否删除源目录
     */
    public static void zip(String zipFileName, List<File> files, String encoding, boolean deleteSrcFileFlag) {
        if (files == null) {
            return;
        }

        FileOutputStream fou = null;
        ArchiveOutputStream archOuts = null;
        try {
            fou = new FileOutputStream(zipFileName);
            archOuts = new ArchiveStreamFactory()
                    .createArchiveOutputStream(ArchiveStreamFactory.ZIP, fou);
            if (archOuts instanceof ZipArchiveOutputStream) {
                ZipArchiveOutputStream zipOut = (ZipArchiveOutputStream) archOuts;
                for (int i = 0; i < files.size(); i++) {
                    ZipArchiveEntry zipEntry = new ZipArchiveEntry(
                            files.get(i), files.get(i).getName());
                    zipOut.putArchiveEntry(zipEntry);
                    zipOut.setEncoding(encoding);
                    FileInputStream in = FileUtils.openInputStream(files.get(i));
                    try {
                        IOUtils.copy(in, zipOut);//这样写才能防止内存溢出!!!
                    } finally {
                        IOUtils.closeQuietly(in);
                    }
//					zipOut.write(FileUtils.readFileToByteArray(files.get(i)));//这一句会内存溢出,谨记!!!
                }
                zipOut.closeArchiveEntry();
                zipOut.flush();
                zipOut.finish();
                IOUtils.closeQuietly(zipOut);
            }

            if (deleteSrcFileFlag) {
                for (int i = 0; i < files.size(); i++) {
                    files.get(i).delete();
                }
            }
        } catch (Exception e) {
            logger.error("ZIP压缩错误", e);
        } finally {
            IOUtils.closeQuietly(fou);
            IOUtils.closeQuietly(archOuts);
        }
    }

    /**
     * 新的解压缩方法,修复了360压缩工具的无序导致解压失败的问题,建议直接用这个
     *
     * @param zipfile 要解压的ZIP文件
     * @param destDir 要解压到的目录,如果传null,默认解压到ZIP文件的目录下
     * @param charset 压缩文件的编码
     * @return 解压出来的文件和目录, 如果返回null, 说明解压失败
     */
    public static List<File> unZip(File zipfile, File destDir, String charset) {
        if (destDir == null) {
            destDir = zipfile.getParentFile();
        }
        ZipArchiveInputStream is = null;
        List<File> files = new ArrayList<File>();
        try {
            is = new ZipArchiveInputStream(new BufferedInputStream(new FileInputStream(zipfile), BUFFER_SIZE), charset);
            ZipArchiveEntry entry;
            while ((entry = is.getNextZipEntry()) != null) {
                if (entry.isDirectory()) {
                    File directory = new File(destDir, entry.getName());
                    directory.mkdirs();
                    files.add(directory);
                } else {
                    String fileName = entry.getName();
                    if (fileName.startsWith("/")) {
                        fileName = fileName.substring(1);
                    }
                    String filePath = destDir.getPath() + '/' + fileName;
                    File file = new File(filePath);
                    //兼容360压缩的坑爹无序问题
                    if (!file.getParentFile().exists()) {
                        file.getParentFile().mkdirs();
                    }

                    OutputStream os = null;
                    try {
                        os = new BufferedOutputStream(new FileOutputStream(file), BUFFER_SIZE);
                        IOUtils.copy(is, os);
                        files.add(file);
                    } finally {
                        IOUtils.closeQuietly(os);
                    }
                }
            }
        } catch (Exception e) {
            logger.error("解压文件{}到{}异常", zipfile, destDir, e);
            return null;
        } finally {
            IOUtils.closeQuietly(is);
        }

        return files;
    }

    /**
     * 递归方式进行文件解压,会将目录或者ZIP压缩包里所有的层次中的ZIP进行解压
     *
     * @param zipFile 压缩文件或者文件夹
     * @param files   最后解压出来的所有文件(非文件夹,非zip文件)
     * @param charset 字符集
     */
    public static void recursionUnZip(File zipFile, File targetDir, List<File> files, String charset) {

        if (zipFile.isDirectory()) {
            File[] listFiles = zipFile.listFiles();
            for (File file : listFiles) {
                if (file.isFile() && "zip".equalsIgnoreCase(FilenameUtils.getExtension(file.getName()))) {
                    File dir = new File(file.getParent() + '/' + FilenameUtils.removeExtension(file.getName()));
                    recursionUnZip(file, dir, files, charset);
                } else if (file.isFile()) {
                    files.add(file);
                } else if (file.isDirectory()) {
                    recursionUnZip(file, file, files, charset);
                }
            }
        } else {
            if (!targetDir.exists()) {
                targetDir.mkdirs();
            }
            List<File> fs = ZipFileUtil.unZip(zipFile, targetDir, charset);
            if (fs == null) {
                return;
            }

            recursionUnZip(targetDir, targetDir, files, charset);
        }
    }

}